package jia.core.processors;

import jia.core.event.AttackEvent;
import jia.core.event.DamageEvent;
import jia.core.event.GameEvent;
import jia.core.event.TextEvent;
import jia.game.GenericFlags;
import jia.core.EventProcessor;
import jia.core.GameStateManager;
import jia.core.flags.DamageEventFlags;
import jia.core.flags.EventFlags;
import jia.core.calculations.Dice;
import jia.core.characters.GameCharacter;
import jia.core.event.eventdata.AttackEventData;
import jia.temp.PrimitiveCombatRunner;
import org.apache.log4j.Logger;

import java.util.GregorianCalendar;

/**
 * Created by IntelliJ IDEA. User: stelle Date: Jan 18, 2006 Time: 12:09:20 AM
 * To change this template use File | Settings | File Templates.
 */
public class BasicEventProcessor implements EventProcessor{
    private static final Logger log = Logger.getLogger(BasicEventProcessor.class);
    
    /**
     * Default Constructor
     */
    public BasicEventProcessor(){
    }
    
    /**
     * @param gameState
     * @param evt
     */
    public void processEvent(GameStateManager gameState, GameEvent evt){
        try {
            if(evt != null && shouldRun(evt)) {
                if(evt instanceof AttackEvent) {
                    ProcessAttackEvent(evt, gameState);
                } else if(evt instanceof TextEvent) {
                    ProcessTextEvent(evt, gameState);
                } else if(evt instanceof DamageEvent) {
                    ProcessDamageEvent(evt, gameState);
                }
            } else {
                // gameState.getUiManager().getDialoguePane().setText("");
            }
        } catch(Exception e) {
            if (GenericFlags.errorMessages){
                log.error(e.getMessage());
            }
            if(GenericFlags.debugMessages) {
                log.debug("An Error occured while processing an event!");
            }
        }
    }
    
    private void ProcessAttackEvent(GameEvent evt, GameStateManager gameState){
        AttackEventData evtData = (AttackEventData) evt.getEventData();
        gameState.getUiManager().setDialogText(gameState.getUiManager().getDialoguePane().getText() + "\n" + GregorianCalendar.HOUR + ":" + GregorianCalendar.MINUTE + ":" + GregorianCalendar.SECOND + " - " + evtData.getEncounterText());
        PrimitiveCombatRunner combatRunner = new PrimitiveCombatRunner();
        combatRunner.runCombat(gameState);
    }
    
    private void ProcessTextEvent(GameEvent evt, GameStateManager gameState){
        TextEvent newEvt = (TextEvent) evt;
        gameState.getUiManager().setDialogText(gameState.getUiManager().getDialoguePane().getText() + "\n" + GregorianCalendar.HOUR + ":" + GregorianCalendar.MINUTE + ":" + GregorianCalendar.SECOND + " - " + newEvt.getEncounterText());
        if (evt.getFlags().hasFlag(EventFlags.IS_CHAINED_TO_ANOTHER_EVENT)) {
            processEvent(gameState, evt.getNextEvent());
        }
        
    }
    
    private void ProcessDamageEvent(GameEvent evt, GameStateManager gameState){
        DamageEvent newDmgEvt = (DamageEvent) evt;
        if(newDmgEvt.getDamageFlags().hasFlag(DamageEventFlags.DAMAGE_RESULT_FROM_SPELL)) {
            //check to see if the spell affects the party or just the leader
            //TODO:  Implement spell damage for damage events
        } else if(newDmgEvt.getDamageFlags().hasFlag(DamageEventFlags.DAMAGE_RESULT_FROM_TRAP)) {
            //this is the result of a trap.  check to see if the party is searching
            if(newDmgEvt.getFlags().hasFlag(EventFlags.NOT_DETECTING_TRAPS) || !newDmgEvt.getFlags().hasFlag(EventFlags.DETECTING_TRAPS)) {
                //check to see if the trap affects just the leader or the entire party
                if(newDmgEvt.getDamageFlags().hasFlag(DamageEventFlags.AFFECTS_ENTIRE_PARTY)) {
                    //process the damage to the entire party
                    gameState.getUiManager().setDialogText(gameState.getUiManager().getDialoguePane().getText() + "\n" + GregorianCalendar.HOUR + ":" + GregorianCalendar.MINUTE + ":" + GregorianCalendar.SECOND + " - The entire party gets hit for damage!");
                    for(int i = 0; i < gameState.getGameCharacters().length; i++) {
                        GameCharacter gameCharacter = gameState.getGameCharacters()[i];
                        
                        int damage = Dice.roll(newDmgEvt.getDamageDice(), newDmgEvt.getNumberDice());
                        //TODO:  insert logic that takes saving throws into account that will moderate the damage
                        gameState.getUiManager().setDialogText(gameState.getUiManager().getDialoguePane().getText() + "\n" + GregorianCalendar.HOUR + ":" + GregorianCalendar.MINUTE + ":" + GregorianCalendar.SECOND + " - " + gameCharacter.getCharacterName() + " was hit for " + Integer.toString(damage));
                        gameCharacter.setCharacterHP(gameCharacter.getCharacterHP() - damage);
                        gameState.getUiManager().getCharacterList().updateTable();
                    }
                } else if(newDmgEvt.getDamageFlags().hasFlag(DamageEventFlags.AFFECTS_LEADER)) {
                    //process the damage to just the leader
                    gameState.getUiManager().setDialogText(gameState.getUiManager().getDialoguePane().getText() + "\n" + GregorianCalendar.HOUR + ":" + GregorianCalendar.MINUTE + ":" + GregorianCalendar.SECOND + " - The leader gets hit for damage!");
                    //TODO:  Enter code for just applying damage to the leader
                    GameCharacter[] characters = gameState.getGameCharacters();
                    characters[0].setCharacterHP(characters[0].getCharacterHP() - Dice.roll(newDmgEvt.getDamageDice(), newDmgEvt.getNumberDice()));
                    gameState.getUiManager().getCharacterList().updateTable();
                }
            } else {
                //if they are detecting traps, and have a thief in the party, then the trap may be disarmed
                //TODO:  Implement logic to disarm a trap
            }
        }
        if (evt.getFlags().hasFlag(EventFlags.IS_CHAINED_TO_ANOTHER_EVENT)) {
            processEvent(gameState, evt.getNextEvent());
        }
        
    }
    
    /**
     * Private logic method used to determine if the event in question should be
     * performed based on various given criteria.
     *
     * @param evt
     *
     * @return
     */
    private boolean shouldRun(GameEvent evt){
        EventFlags evtFlags = evt.getFlags();
        
        // If it's a once-only event...
        if(evtFlags.hasFlag(EventFlags.ONLY_ONCE)) {
            // And hasn't yet executed
            if(!evtFlags.hasFlag(EventFlags.EVENT_TRIGGERED)) {
                evtFlags.setFlag(EventFlags.EVENT_TRIGGERED);
                return true;
            }
        }
        
        // TODO:  More logic here.  For now, assume we shouldn't run
        return false;
    }
}
